#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "pbmpak.h"

FILE *xfopen(char *, char *);
void *xcalloc(unsigned long int, int);
int *box_blur(int, int, int *);
int box_blur_aux_h(int, int, int, int, int *);
int box_blur_aux_v(int, int, int, int, int *);

typedef struct
{
    int xsize;
    int ysize;
    int maxrgb;
} IMG_HEADER;

typedef struct
{
    int *rarray;
    int *garray;
    int *barray;
} IMG_DATA;

int main(int argc, char *argv[])
{
    FILE *img = NULL;
    IMG_HEADER header;
    IMG_DATA datain, dataout;
    char *filename_in = NULL, *filename_out = NULL;
    int size = 0;

    memset(&header, 0, sizeof(IMG_HEADER));
    memset(&datain, 0, sizeof(IMG_DATA));

    if(argc > 1)
    {
        filename_in = &argv[1][0];

        if(argc > 2)
        {
            filename_out = &argv[2][0];
        }
        else
        {
            filename_out = xcalloc(strlen("out.ppm"), sizeof(char));
            strcpy(filename_out, "out.ppm");
        }
    }
    else
    {
        filename_in = xcalloc(strlen("in.ppm"), sizeof(char));
        strcpy(filename_in, "in.ppm");

        filename_out = xcalloc(strlen("out.ppm"), sizeof(char));
        strcpy(filename_out, "out.ppm");
    }

    img = xfopen(filename_in, "r");

    ppma_read_header(img, &(header.xsize), &(header.ysize), &(header.maxrgb));

    size = header.xsize * header.ysize;
    datain.rarray = xcalloc(size, sizeof(int));
    datain.garray = xcalloc(size, sizeof(int));
    datain.barray = xcalloc(size, sizeof(int));

    ppma_read_data(img, header.xsize, header.ysize, datain.rarray, datain.garray, datain.barray);

    dataout.rarray = box_blur(header.xsize, header.ysize, datain.rarray);
    dataout.garray = box_blur(header.xsize, header.ysize, datain.garray);
    dataout.barray = box_blur(header.xsize, header.ysize, datain.barray);

    ppma_write (filename_out, header.xsize, header.ysize, header.maxrgb,
                dataout.rarray, dataout.garray, dataout.barray);

    free(filename_in);
    free(filename_out);

    free(datain.rarray);
    free(datain.garray);
    free(datain.barray);

    free(dataout.rarray);
    free(dataout.garray);
    free(dataout.barray);

    return 0;
}

int *box_blur(int xsize, int ysize, int *array)
{
    int *result_temp = NULL, *result = NULL;
    int i, j;

    result_temp = xcalloc(xsize * ysize, sizeof(int));
    result = xcalloc(xsize * ysize, sizeof(int));

    for(i = 0; i < xsize; i++)
    {
        for(j = 0; j < ysize; j++)
        {
            result_temp[i * xsize + j] = box_blur_aux_h(i, j, xsize, ysize, array);
        }
    }

    for(i = 0; i < xsize; i++)
    {
        for(j = 0; j < ysize; j++)
        {
            result[i * xsize + j] = box_blur_aux_v(i, j, xsize, ysize, result_temp);
        }
    }

    free(result_temp);

    return result;
}

int box_blur_aux_h(int row, int col, int xsize, int ysize, int *array)
{
    int i, accumulation = 0, average = 0;

    for(i = col - 2; i < (col - 2) + 5; i++)
    {
        if(i < 0 || i >= xsize) continue;

        accumulation += array[row * xsize + i];
    }

    average = accumulation / 5;

    return average;
}

int box_blur_aux_v(int row, int col, int xsize, int ysize, int *array)
{
    int i, accumulation = 0, average = 0;

    for(i = row - 2; i < (row - 2) + 5; i++)
    {
        if(i < 0 || i >= ysize) continue;

        accumulation += array[i * xsize + col];
    }

    average = accumulation / 5;

    return average;
}

FILE *xfopen(char *filename, char *mode)
{
    FILE *ptr = NULL;

    ptr = fopen(filename, mode);

    if(!ptr)
    {
        fprintf(stderr, "%s: %d: %s\n", __FILE__, __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    return ptr;
}

void *xcalloc(unsigned long int num, int size)
{
    void *ptr = NULL;

    ptr = calloc(num, size);

    if(!ptr)
    {
        fprintf(stderr, "%s: %d: %s\n", __FILE__, __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    return ptr;
}
